home *** CD-ROM | disk | FTP | other *** search
- unit PlatoniF;
-
- interface
-
- uses
- Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
- Math, StdCtrls, ExtCtrls, ComCtrls;
-
- const
- Xmin = -10;
- Xmax = 10;
- Ymin = -10;
- Ymax = 10;
-
- type
- TMatrix3D = array [1..4, 1..4] of Single;
- TVector3D = array [1..4] of Single;
- TPoint3D = record
- Coord : TVector3D; // The untransformed coordinates.
- Trans : TVector3D; // The transformed coordinates.
- end;
-
- TPlatonicForm = class(TForm)
- SolidOption: TRadioGroup;
- ShowAxesCheck: TCheckBox;
- RotateLeftRight: TUpDown;
- RotateUpDown: TUpDown;
- procedure FormCreate(Sender: TObject);
- procedure MakeIdentity(var M : TMatrix3D);
- procedure MatrixMatrixMult(var R : TMatrix3D; A, B : TMatrix3D);
- procedure VectorMatrixMult(var r : TVector3D; p : TVector3D; A : TMatrix3D);
- procedure FormPaint(Sender: TObject);
- procedure BuildTransformation(var T : TMatrix3D);
- procedure AddSegment(x1, y1, z1, x2, y2, z2 : Single);
- function SidesUneven(seg1, seg2 : Integer) : Boolean;
- procedure ShowMatrix(M : TMatrix3D);
- procedure FormKeyDown(Sender: TObject; var Key: Word;
- Shift: TShiftState);
- procedure SolidOptionClick(Sender: TObject);
- procedure ShowAxesCheckClick(Sender: TObject);
- procedure RotateLeftRightClick(Sender: TObject; Button: TUDBtnType);
- procedure RotateUpDownClick(Sender: TObject; Button: TUDBtnType);
-
- private
- { Private declarations }
- EyeX, EyeY, EyeZ : Single; // Coordinates of viewing eye.
-
- // Line segment coordinates.
- Segments : array [0..1000, 1..2] of TPoint3D;
- NumSegments : Integer;
-
- // Index of first segment in each polytope.
- FirstSegment : array[0..6] of Integer;
-
- public
- { Public declarations }
- end;
-
- var
- PlatonicForm: TPlatonicForm;
-
- implementation
-
- {$R *.DFM}
-
- // Create some data to display.
- procedure TPlatonicForm.FormCreate(Sender: TObject);
- var
- theta1, theta2, s1, s2, c1, c2 : Single;
- S, R, H, A, B, C, D, x, y, y2, M, N : Single;
- begin
- // Initialize the viewing eye
- EyeX := 40;
- EyeY := 20;
- EyeZ := 20;
-
- // Initialize the segment data.
- NumSegments := 0;
-
- // Save segments for the axes.
- FirstSegment[0] := NumSegments;
- AddSegment(0, 0, 0, 2, 0, 0); // X axis.
- AddSegment(0, 0, 0, 0, 1, 0); // Y axis.
- AddSegment(0, 0, 0, 0, 0, 1); // Z axis.
-
- // Tetrahedron.
- FirstSegment[1] := NumSegments;
- S := Sqrt(6);
- A := S / Sqrt(3);
- B := -A / 2;
- C := A * Sqrt(2) - 1;
- D := S / 2;
- AddSegment(0, C, 0, A, -1, 0);
- AddSegment(0, C, 0, B, -1, D);
- AddSegment(0, C, 0, B, -1, -D);
- AddSegment(B, -1, -D, B, -1, D);
- AddSegment(B, -1, D, A, -1, 0);
- AddSegment(A, -1, 0, B, -1, -D);
-
- // Cube.
- FirstSegment[2] := NumSegments;
- AddSegment(-1, -1, -1, -1, 1, -1);
- AddSegment(-1, 1, -1, 1, 1, -1);
- AddSegment(1, 1, -1, 1, -1, -1);
- AddSegment(1, -1, -1, -1, -1, -1);
-
- AddSegment(-1, -1, 1, -1, 1, 1);
- AddSegment(-1, 1, 1, 1, 1, 1);
- AddSegment(1, 1, 1, 1, -1, 1);
- AddSegment(1, -1, 1, -1, -1, 1);
-
- AddSegment(-1, -1, -1, -1, -1, 1);
- AddSegment(-1, 1, -1, -1, 1, 1);
- AddSegment(1, 1, -1, 1, 1, 1);
- AddSegment(1, -1, -1, 1, -1, 1);
-
- // Octahedron.
- FirstSegment[3] := NumSegments;
- AddSegment(0, 1, 0, 1, 0, 0);
- AddSegment(0, 1, 0, -1, 0, 0);
- AddSegment(0, 1, 0, 0, 0, 1);
- AddSegment(0, 1, 0, 0, 0, -1);
-
- AddSegment(0, -1, 0, 1, 0, 0);
- AddSegment(0, -1, 0, -1, 0, 0);
- AddSegment(0, -1, 0, 0, 0, 1);
- AddSegment(0, -1, 0, 0, 0, -1);
-
- AddSegment(0, 0, 1, 1, 0, 0);
- AddSegment(0, 0, 1, -1, 0, 0);
- AddSegment(0, 0, -1, 1, 0, 0);
- AddSegment(0, 0, -1, -1, 0, 0);
-
- // Dodecahedron.
- FirstSegment[4] := NumSegments;
- theta1 := PI * 0.4;
- theta2 := PI * 0.8;
- s1 := Sin(theta1);
- c1 := Cos(theta1);
- s2 := Sin(theta2);
- c2 := Cos(theta2);
-
- M := 1 - (2 - 2 * c1 - 4 * s1 * s1) / (2 * c1 - 2);
- N := Sqrt((2 - 2 * c1) - M * M) * (1 + (1 - c2) / (c1 - c2));
- R := 2 / N;
- S := R * Sqrt(2 - 2 * c1);
- A := R * s1;
- B := R * s2;
- C := R * c1;
- D := R * c2;
-
- x := (R * R * (2 - 2 * c1) - 4 * A * A) / (2 * C - 2 * R);
- y := Sqrt(S * S - (R - x) * (R - x));
- y2 := y * (1 - c2) / (c1 - c2);
-
- AddSegment(R, 1, 0, C, 1, A); // Top
- AddSegment(C, 1, A, D, 1, B);
- AddSegment(D, 1, B, D, 1, -B);
- AddSegment(D, 1, -B, C, 1, -A);
- AddSegment(C, 1, -A, R, 1, 0);
-
- AddSegment(R, 1, 0, x, 1 - y, 0); // Top downward edges.
- AddSegment(C, 1, A, x * c1, 1 - y, x * s1);
- AddSegment(C, 1, -A, x * c1, 1 - y, -x * s1);
- AddSegment(D, 1, B, x * c2, 1 - y, x * s2);
- AddSegment(D, 1, -B, x * c2, 1 - y, -x * s2);
-
- AddSegment(x, 1 - y, 0, -x * c2, 1 - y2, -x * s2); // Middle.
- AddSegment(x, 1 - y, 0, -x * c2, 1 - y2, x * s2);
- AddSegment(x * c1, 1 - y, x * s1, -x * c2, 1 - y2, x * s2);
- AddSegment(x * c1, 1 - y, x * s1, -x * c1, 1 - y2, x * s1);
- AddSegment(x * c2, 1 - y, x * s2, -x * c1, 1 - y2, x * s1);
- AddSegment(x * c2, 1 - y, x * s2, -x, 1 - y2, 0);
- AddSegment(x * c2, 1 - y, -x * s2, -x, 1 - y2, 0);
- AddSegment(x * c2, 1 - y, -x * s2, -x * c1, 1 - y2, -x * s1);
- AddSegment(x * c1, 1 - y, -x * s1, -x * c1, 1 - y2, -x * s1);
- AddSegment(x * c1, 1 - y, -x * s1, -x * c2, 1 - y2, -x * s2);
-
- AddSegment(-R, -1, 0, -x, 1 - y2, 0); // Bottom upward edges.
- AddSegment(-C, -1, A, -x * c1, 1 - y2, x * s1); // Bottom upward edges.
- AddSegment(-D, -1, B, -x * c2, 1 - y2, x * s2);
- AddSegment(-D, -1, -B, -x * c2, 1 - y2, -x * s2);
- AddSegment(-C, -1, -A, -x * c1, 1 - y2, -x * s1);
-
- AddSegment(-R, -1, 0, -C, -1, A); // Bottom
- AddSegment(-C, -1, A, -D, -1, B);
- AddSegment(-D, -1, B, -D, -1, -B);
- AddSegment(-D, -1, -B, -C, -1, -A);
- AddSegment(-C, -1, -A, -R, -1, 0);
-
- // Icosahedron.
- FirstSegment[5] := NumSegments;
- R := 2 / (2 * Sqrt(1 - 2 * c1) + Sqrt(3 / 4 * (2 - 2 * c1) - 2 * c2 - c2 * c2 - 1));
- S := R * Sqrt(2 - 2 * c1);
- H := 1 - Sqrt(S * S - R * R);
- A := R * s1;
- B := R * s2;
- C := R * c1;
- D := R * c2;
- AddSegment(R, H, 0, C, H, A); // Top
- AddSegment(C, H, A, D, H, B);
- AddSegment(D, H, B, D, H, -B);
- AddSegment(D, H, -B, C, H, -A);
- AddSegment(C, H, -A, R, H, 0);
- AddSegment(R, H, 0, 0, 1, 0); // Point
- AddSegment(C, H, A, 0, 1, 0);
- AddSegment(D, H, B, 0, 1, 0);
- AddSegment(D, H, -B, 0, 1, 0);
- AddSegment(C, H, -A, 0, 1, 0);
-
- AddSegment(-R, -H, 0, -C, -H, A); // Bottom
- AddSegment(-C, -H, A, -D, -H, B);
- AddSegment(-D, -H, B, -D, -H, -B);
- AddSegment(-D, -H, -B, -C, -H, -A);
- AddSegment(-C, -H, -A, -R, -H, 0);
- AddSegment(-R, -H, 0, 0, -1, 0); // Point
- AddSegment(-C, -H, A, 0, -1, 0);
- AddSegment(-D, -H, B, 0, -1, 0);
- AddSegment(-D, -H, -B, 0, -1, 0);
- AddSegment(-C, -H, -A, 0, -1, 0);
-
- AddSegment(R, H, 0, -D, -H, B); // Middle
- AddSegment(R, H, 0, -D, -H, -B);
- AddSegment(C, H, A, -D, -H, B);
- AddSegment(C, H, A, -C, -H, A);
- AddSegment(D, H, B, -C, -H, A);
- AddSegment(D, H, B, -R, -H, 0);
- AddSegment(D, H, -B, -R, -H, 0);
- AddSegment(D, H, -B, -C, -H, -A);
- AddSegment(C, H, -A, -C, -H, -A);
- AddSegment(C, H, -A, -D, -H, -B);
- FirstSegment[6] := NumSegments;
-
- // Verify that the side lengths are all the same.
- if (SidesUneven(FirstSegment[1], FirstSegment[2] - 1)) then ShowMessage('Error in tetrahedron.');
- if (SidesUneven(FirstSegment[2], FirstSegment[3] - 1)) then ShowMessage('Error in cube.');
- if (SidesUneven(FirstSegment[3], FirstSegment[4] - 1)) then ShowMessage('Error in octahedron.');
- if (SidesUneven(FirstSegment[4], FirstSegment[5] - 1)) then ShowMessage('Error in dodecahedron.');
- if (SidesUneven(FirstSegment[5], FirstSegment[6] - 1)) then ShowMessage('Error in icosahedron.');
- end;
-
- // Add a segment to the list.
- procedure TPlatonicForm.AddSegment(x1, y1, z1, x2, y2, z2 : Single);
- begin
- with Segments[NumSegments, 1] do
- begin
- Coord[1] := x1;
- Coord[2] := y1;
- Coord[3] := z1;
- Coord[4] := 1.0;
- end;
- with Segments[NumSegments, 2] do
- begin
- Coord[1] := x2;
- Coord[2] := y2;
- Coord[3] := z2;
- Coord[4] := 1.0;
- end;
- NumSegments := NumSegments + 1;
- end;
-
- // Return True if the bounded segments do not all have
- // the same length.
- function TPlatonicForm.SidesUneven(seg1, seg2 : Integer) : Boolean;
-
- function SegmentLength(seg : Integer) : Single;
- var
- x1, y1, z1, x2, y2, z2, dx, dy, dz : Single;
- begin
- with Segments[seg1, 1] do
- begin
- x1 := Coord[1];
- y1 := Coord[2];
- z1 := Coord[3];
- end;
- with Segments[seg1, 2] do
- begin
- x2 := Coord[1];
- y2 := Coord[2];
- z2 := Coord[3];
- end;
- dx := x2 - x1;
- dy := y2 - y1;
- dz := z2 - z1;
- Result := Sqrt(dx * dx + dy * dy + dz * dz);
- end;
-
- var
- len : Single;
- i : Integer;
- begin
- // Get the first segment's length.
- len := SegmentLength(seg1);
-
- // Compare this to the lengths of the other segments.
- for i := seg1 + 1 to seg2 do
- if (Abs(SegmentLength(i) - len) > 0.1) then
- begin
- Result := True;
- exit;
- end;
-
- Result := False;
- end;
-
- // Make M an identity matrix.
- procedure TPlatonicForm.MakeIdentity(var M : TMatrix3D);
- var
- i, j : Integer;
- begin
- for i := 1 to 4 do
- for j := 1 to 4 do
- if (i = j) then
- M[i, j] := 1.0
- else
- M[i, j] := 0.0;
- end;
-
- // Perform matrix-matrix multiplication. Set R = A * B.
- procedure TPlatonicForm.MatrixMatrixMult(var R : TMatrix3D; A, B : TMatrix3D);
- var
- i, j, k : Integer;
- value : Single;
- begin
- for i := 1 to 4 do
- for j := 1 to 4 do
- begin
- // Calculate R[i, j].
- value := 0.0;
- for k := 1 to 4 do
- value := value + A[i, k] * B[k, j];
- R[i, j] := value;
- end;
- end;
-
- // Perform vector-matrix multiplication. Set r = p * A.
- procedure TPlatonicForm.VectorMatrixMult(var r : TVector3D; p : TVector3D; A : TMatrix3D);
- var
- i, j : Integer;
- value : Single;
- begin
- for i := 1 to 4 do
- begin
- value := 0.0;
- for j := 1 to 4 do
- value := value + p[j] * A[j, i];
- r[i] := value;
- end;
-
- // normalize the point. Note value still holds r[4].
- r[1] := r[1] / value;
- r[2] := r[2] / value;
- r[3] := r[3] / value;
- r[4] := 1.0;
- end;
-
- // Draw the selected solid.
- procedure TPlatonicForm.FormPaint(Sender: TObject);
- var
- i, seg1, seg2 : Integer;
- T : TMatrix3D;
- rect : TRect;
- begin
- // Build the transformation.
- BuildTransformation(T);
-
- // Erase the form.
- rect.Left := 0;
- rect.Top := 0;
- rect.Right := ClientWidth;
- rect.Bottom := ClientHeight;
- Canvas.Brush.Color := Color;
- Canvas.FillRect(rect);
-
- // Draw the selected solid's segments.
- Canvas.Pen.Color := clBlack;
- i := SolidOption.ItemIndex + 1;
- seg1 := FirstSegment[i];
- seg2 := FirstSegment[i + 1] - 1;
- for i := seg1 to seg2 do
- begin
- // Apply the transformation to the points.
- VectorMatrixMult(Segments[i, 1].Trans,
- Segments[i, 1].Coord, T);
- VectorMatrixMult(Segments[i, 2].Trans,
- Segments[i, 2].Coord, T);
-
- // Draw the segment.
- Canvas.MoveTo(Round(Segments[i, 1].Trans[1]),
- Round(Segments[i, 1].Trans[2]));
- Canvas.LineTo(Round(Segments[i, 2].Trans[1]),
- Round(Segments[i, 2].Trans[2]));
- end;
-
- // Draw the axes if desired.
- if (ShowAxesCheck.Checked) then
- begin
- Canvas.Pen.Color := clGreen;
- for i := FirstSegment[0] to FirstSegment[1] - 1 do
- begin
- // Apply the transformation to the points.
- VectorMatrixMult(Segments[i, 1].Trans,
- Segments[i, 1].Coord, T);
- VectorMatrixMult(Segments[i, 2].Trans,
- Segments[i, 2].Coord, T);
-
- // Draw the segments.
- Canvas.MoveTo(Round(Segments[i, 1].Trans[1]),
- Round(Segments[i, 1].Trans[2]));
- Canvas.LineTo(Round(Segments[i, 2].Trans[1]),
- Round(Segments[i, 2].Trans[2]));
- end;
- end;
- end;
-
- // Build a transformation matrix for display.
- procedure TPlatonicForm.BuildTransformation(var T : TMatrix3D);
- var
- r1, r2, ctheta, stheta, cphi, sphi : Single;
- T1, T2, T3, T4, T12, T34 : TMatrix3D;
- begin
- // Rotate around the Z axis until the eye lies in
- // the Y-Z plane.
- r1 := Sqrt(EyeX * EyeX + EyeY * EyeY);
- stheta := EyeX / r1;
- ctheta := EyeY / r1;
- MakeIdentity(T1);
- T1[1, 1] := ctheta;
- T1[1, 2] := stheta;
- T1[2, 1] := -stheta;
- T1[2, 2] := ctheta;
-
- // Rotate around the X axis until the eye lies within
- // the Z axis.
- r2 := Sqrt(EyeX * EyeX + EyeY * EyeY + EyeZ * EyeZ);
- sphi := -r1 / r2;
- cphi := -EyeZ / r2;
- MakeIdentity(T2);
- T2[2, 2] := cphi;
- T2[2, 3] := sphi;
- T2[3, 2] := -sphi;
- T2[3, 3] := cphi;
-
- // We could project along the Z axis here. Instead we
- // just ignore the Z coordinate when drawing.
-
- // Make the picture reasonably large on the form.
- // Here we scale y by -50 to reverse its sign since
- // the Canvas starts with (0, 0) in the upper left.
- MakeIdentity(T3);
- T3[1, 1] := 50;
- T3[2, 2] := -50;
- T3[3, 3] := 50;
-
- // Center the picture on the form.
- MakeIdentity(T4);
- r1 := SolidOption.Width + SolidOption.Left;
- T4[4, 1] := (ClientWidth - r1) / 2 + r1;
- T4[4, 2] := ClientHeight / 2;
-
- // Combine the transformations.
- MatrixMatrixMult(T12, T1, T2);
- MatrixMatrixMult(T34, T3, T4);
- MatrixMatrixMult(T, T12, T34);
- end;
-
- // This procedure is useful for debugging.
- procedure TPlatonicForm.ShowMatrix(M : TMatrix3D);
- var
- i, j : Integer;
- txt : String;
- begin
- txt := '';
- for i := 1 to 4 do
- begin
- for j := 1 to 4 do
- txt := txt + Format('%4.2f ', [M[i, j]]);
- txt := txt + #10#13;
- end;
- ShowMessage(txt);
- end;
-
- // Adjust the eye position if it's an arrow key.
- procedure TPlatonicForm.FormKeyDown(Sender: TObject; var Key: Word;
- Shift: TShiftState);
- const
- PI = 3.14159;
- Dtheta = PI / 16;
- Dphi = PI / 16;
- var
- theta, phi, r1, r2 : Single;
- begin
- if (not (Key in [VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT]))
- then exit;
-
- theta := ArcTan2(EyeY, EyeX);
- r1 := Sqrt(EyeX * EyeX + EyeY * EyeY);
- r2 := Sqrt(EyeX * EyeX + EyeY * EyeY + EyeZ * EyeZ);
- phi := ArcTan2(EyeZ, r1);
-
- case Key of
- VK_LEFT:
- theta := theta - Dtheta;
-
- VK_UP:
- begin
- phi := phi + Dphi;
- if (phi > PI / 2) then phi := PI / 2;
- end;
-
- VK_RIGHT:
- theta := theta + Dtheta;
-
- VK_DOWN:
- begin
- phi := phi - Dphi;
- if (phi < -PI / 2) then phi := -PI / 2;
- end;
- end;
-
- EyeX := r1 * Cos(theta);
- EyeY := r1 * Sin(theta);
- EyeZ := r2 * Sin(phi);
- Refresh;
- end;
-
- // Redraw to show the new selection.
- procedure TPlatonicForm.SolidOptionClick(Sender: TObject);
- begin
- Refresh;
- end;
-
- // Redraw to show the new selection.
- procedure TPlatonicForm.ShowAxesCheckClick(Sender: TObject);
- begin
- Refresh;
- end;
-
- procedure TPlatonicForm.RotateLeftRightClick(Sender: TObject;
- Button: TUDBtnType);
- const
- PI = 3.14159;
- Dtheta = PI / 16;
- var
- theta, r1 : Single;
- begin
- theta := ArcTan2(EyeY, EyeX);
- r1 := Sqrt(EyeX * EyeX + EyeY * EyeY);
-
- if (Button = btNext) then
- // Right.
- theta := theta + Dtheta
- else
- // Left.
- theta := theta - Dtheta;
-
- EyeX := r1 * Cos(theta);
- EyeY := r1 * Sin(theta);
- Refresh;
- end;
-
- procedure TPlatonicForm.RotateUpDownClick(Sender: TObject;
- Button: TUDBtnType);
- const
- PI = 3.14159;
- Dphi = PI / 16;
- var
- theta, phi, r1, r2 : Single;
- begin
- theta := ArcTan2(EyeY, EyeX);
- r1 := Sqrt(EyeX * EyeX + EyeY * EyeY);
- r2 := Sqrt(EyeX * EyeX + EyeY * EyeY + EyeZ * EyeZ);
- phi := ArcTan2(EyeZ, r1);
-
- if (Button = btNext) then
- begin
- phi := phi + Dphi;
- if (phi > PI / 2) then phi := PI / 2;
- end else begin
- phi := phi - Dphi;
- if (phi < -PI / 2) then phi := -PI / 2;
- end;
-
- EyeX := r1 * Cos(theta);
- EyeY := r1 * Sin(theta);
- EyeZ := r2 * Sin(phi);
- Refresh;
- end;
-
- end.
-